﻿using System;
using System.Net;
using System.Threading.Tasks;
using Extention.ENode.Rpc.Runtime.Communally.Entitys.Messages;
using Extention.ENode.Rpc.Transport.Codec;
using Extention.ENode.Rpc.Transport.InternalAdaper;
using DotNetty.Codecs;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Microsoft.Extensions.Logging;
using DotNetty.Buffers;

namespace Extention.ENode.Rpc.Transport.Impl
{
    /// <summary>
    /// 基于DotNetty的消息监听者
    /// </summary>
    public class DefaultDotNettyServerMessageListener : IMessageListener, IDisposable
    {
        private readonly ITransportMessageDecoder _transportMessageDecoder;
        private readonly ITransportMessageEncoder _transportMessageEncoder;
        private readonly ILogger _logger;
        private readonly int _lengthFieldPrepender = 4;
        private readonly int _basedFrame = 4;
        private IChannel _channel;

        public DefaultDotNettyServerMessageListener(ITransportMessageCodecFactory codecFactory,
            ILogger<DefaultDotNettyServerMessageListener> logger)
        {
            _transportMessageEncoder = codecFactory.GetEncoder();
            _transportMessageDecoder = codecFactory.GetDecoder();
            _logger = logger;
        }

        public event ReceivedDelegate Received;

        /// <summary>
        /// 触发接收到消息事件
        /// </summary>
        /// <param name="sender">消息发送者</param>
        /// <param name="message">接收到的消息</param>
        /// <returns>一个任务</returns>
        public async Task OnReceived(IMessageSender sender, TransportMessage message)
        {
            _logger.LogInformation("当前获取消息事件是否为空:" + Received == null ? "是" : "否");
            if (Received == null) return;
            await Received(sender, message);
        }

        public async Task StartAsync(EndPoint endPoint)
        {
            _logger.LogInformation($"准备启动服务主机，监听地址：{endPoint}");

            IEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1);
            IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();
            var bootstrap = new ServerBootstrap();
            bootstrap
                .Channel<TcpServerSocketChannel>()
                .Option(ChannelOption.SoBacklog, 8192)
                .ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
                .Group(bossGroup, workerGroup)
                .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                {
                    var pipeline = channel.Pipeline;
                    pipeline.AddLast(new LengthFieldPrepender(_lengthFieldPrepender));
                    pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, _basedFrame, 0, _basedFrame));
                    pipeline.AddLast(new TransportMessageChannelHandlerDecodeAdapter(_transportMessageDecoder));
                    pipeline.AddLast(new ServerHandler(async (contenxt, message) =>
                        {
                            var sender = new DefaultDotNettyServerMessageSender(_transportMessageEncoder, contenxt);
                            await OnReceived(sender, message);
                        },
                        _logger));
                }));
            _channel = await bootstrap.BindAsync(endPoint);
            _logger.LogInformation($"服务主机启动成功，监听地址：{endPoint}");
        }
        public void CloseAsync()
        {
            Task.Run(async () =>
            {
                await _channel.EventLoop.ShutdownGracefullyAsync();
                await _channel.CloseAsync();
            }).Wait();
        }
        public void Dispose()
        {
            Task.Run(async () =>
            {

                await _channel.DisconnectAsync();
            }).Wait();
        }

        #region Help Class

        private class ServerHandler : ChannelHandlerAdapter
        {
            private readonly Action<IChannelHandlerContext, TransportMessage> _readAction;
            private readonly ILogger _logger;

            public ServerHandler(Action<IChannelHandlerContext, TransportMessage> readAction, ILogger logger)
            {
                _readAction = readAction;
                _logger = logger;
            }

            #region Overrides of ChannelHandlerAdapter

            public override void ChannelRead(IChannelHandlerContext context, object message)
            {
                Task.Run(() =>
                {
                    var transportMessage = (TransportMessage)message;
                    _readAction(context, transportMessage);
                });
            }

            public override void ChannelReadComplete(IChannelHandlerContext context)
            {
                context.Flush();
            }

            public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
            {
                context.CloseAsync();//客户端主动断开需要应答，否则socket变成CLOSE_WAIT状态导致socket资源耗尽
                if (_logger.IsEnabled(LogLevel.Error))
                    _logger.LogError(exception, $"与服务器：{context.Channel.RemoteAddress}通信时发送了错误。");
            }

            #endregion Overrides of ChannelHandlerAdapter
        }

        #endregion Help Class
    }
}